/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.openide.compiler;
import java.io.IOException;
import java.util.LinkedList;
import java.util.HashMap;
import java.util.Iterator;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.OutputStreamWriter;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Collection;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Arrays;
import java.util.*;
import org.openide.execution.*;
import org.openide.filesystems.FileSystemCapability;
import org.openide.filesystems.FileStateInvalidException;
import org.openide.filesystems.FileObject;
import org.openide.util.MapFormat;
import org.openide.util.Utilities;
/** A group holding several <code>ExternalCompiler</code>s.
* When they are compiled, all the filename arguments are collected and the process is run
* only once.
* @see ExternalCompiler
*
* @author Ales Novak, Jaroslav Tulach
*/
public class ExternalCompilerGroup extends CompilerGroup {
/** The compilers to be used.
* @associates Compiler*/
private Set compilers = new HashSet (7); // Set<ExternalCompiler>
/** flag for indicating errors */
private boolean dirty;
/** dimension */
//private static final int DIM = 7;
/** Create an external compiler group. */
public ExternalCompilerGroup() {
}
/* Consumes a compiler. Should absorb all information
* contained in the compiler.
*
* @param c an instance of ExternalCompiler
* @exception IllegalArgumentException if this compiler
* does not belong to this group (the group's class is not the
* same as the one returned from c.compilerGroupClass)
*/
public void add (Compiler c) throws IllegalArgumentException {
if (! (c instanceof ExternalCompiler)) {
throw new IllegalArgumentException();
}
compilers.add (c);
//System.err.println("ExternalCompilerGroup.add; c=" + c);
}
/** Allows subclasses to provide their own format for parsing
* the arguments of NbProcessDescriptor contained in the
* ExternalCompiler; assumes interesting content of "compiler type".
* <P> By default, delegates to the variant that does not take a
* "compiler type" argument, as this is deprecated usage.
*
* @param desc description of program to start
* @param files the argument to compiler list of files to compile (or reference
* to the file with @files)
* @param compilerType the type of compiler for all this files,
* this is the compiler dependent object returned from method
* ExternalCompiler.compilerType ()
* @return format to use for changing the command line of the compiler
* @exception IOException if exec fails
*
* @see ExternalCompiler#compilerType
*
* @deprecated Please instead directly override {@link #createProcess(NbProcessDescriptor,String[])}
* as this version does not use the now-deprecated "compiler type" object.
*/
protected Process createProcess (
NbProcessDescriptor desc, String[] files, Object compilerType
) throws IOException {
return createProcess (desc, files);
}
/** Allows subclasses to provide their own format for parsing
* the arguments of NbProcessDescriptor contained in the
* ExternalCompiler.
* <P>
* This implementation creates new format Format with settings
* from NbClassPath.createXXXX and executes them in the provided
* process descriptor.
*
* @param desc description of program to start
* @param files the argument to compiler list of files to compile (or reference
* to the file with @files)
* @return format to use for changing the command line of the compiler
* @exception IOException if exec fails
*/
protected Process createProcess (
NbProcessDescriptor desc, String[] files
) throws IOException {
return desc.exec (new Format (files));
}
/** Creates human readable String used in status line - should contain
* information what is compiled - Compiling MyClass.java
*
* @return String
*/
protected String getStatusLineText() {
String msg;
if (compilers.size() == 1) {
FileObject fo = getAllCompilers()[0].getFileObject();
msg = java.text.MessageFormat.format(
getString("CTL_FMT_CompilingMessage"),
new Object[] { fo.getPackageName('.') });
} else {
msg = getString("FMT_GenericCompilingMessage");
}
return msg;
}
/**
* @return an array containing all Compilers that were added to this
* CompilerGroup
*/
protected final ExternalCompiler[] getAllCompilers() {
return (ExternalCompiler[]) compilers.toArray(new ExternalCompiler[compilers.size()]);
}
/** access method for firing errors */
void fireErrEvent(ErrorEvent ev) {
if (ev.getFile() != null) {
dirty = true;
}
fireErrorEvent(ev);
}
/* Starts compilation. It should check which files realy needs to be
* compiled and compile only those which really need to.
* <P>
* The compilation should fire info to status listeners and report
* all errors to error listeners.
*
* @return true if successful, false otherwise
*/
public boolean start () {
//System.err.println("ExternalCompilerGroup.start; this=" + this + "; thread=" + Thread.currentThread ());
dirty = false;
if (compilers.isEmpty ()) return true; // if no compilers have been added -> succeed
CompilerExecutor cexec = null;
Set folders = new HashSet (7); // Set<FileObject>
Iterator it = compilers.iterator ();
while (it.hasNext ()) {
ExternalCompiler c = (ExternalCompiler) it.next ();
if (cexec == null)
cexec = new CompilerExecutor (this,
c.getCompilerDescriptor (),
c.getErrorExpression (),
c.compilerType ());
String name = c.getFileName ();
//System.err.println("\tname=" + name + "; this=" + this);
if (name != null && ! "".equals (name)) cexec.addFile (name); // NOI18N
FileObject folder = c.getFileObject ();
if (folder != null) {
if (folder.isData ()) {
folder = folder.getParent ();
}
folders.add (folder);
}
}
try {
//System.err.println("\twill execute; this=" + this);
int result = cexec.execute(new ExecInfo("ExternalCompilerGroup")).result(); // NOI18N
//System.err.println("\tresult=" + result + "; this=" + this);
dirty |= (result != 0);
// refresh folders
if (! dirty) {
it = folders.iterator ();
while (it.hasNext ())
((FileObject) it.next ()).refresh ();
}
if (result == CompilerSysProcess.INTERRUPTED) {
ErrorEvent ev = new ErrorEvent(this, null, -1, -1, getString("CTL_Interrupted"), null);
fireErrorEvent(ev);
}
} catch (IOException ioe) {
StringWriter swriter = new StringWriter();
PrintWriter pw = new PrintWriter(swriter);
ioe.printStackTrace(pw);
fireErrorEvent (new ErrorEvent (
this,
null, // wrong
0, 0,
swriter.toString(),
"" // NOI18N
));
dirty = true;
}
return !dirty; // dirty == false means success
}
private static java.util.ResourceBundle bundle;
private static String getString(String x) {
if (bundle == null) {
bundle = org.openide.util.NbBundle.getBundle(ExternalCompilerType.class);
}
return bundle.getString(x);
}
/** Default format that can format tags related to compilation. These include settings of classpath
* (can be composed from repository, class path, boot class path and libraries) and
* putting somewhere list of files to compile.
*/
public static class Format extends MapFormat {
/** Tag replaced with ProcessExecutors.getClassPath () */
public static final String TAG_CLASSPATH = ProcessExecutor.Format.TAG_CLASSPATH;
/** Tag replaced with ProcessExecutors.getBootClassPath () */
public static final String TAG_BOOTCLASSPATH = ProcessExecutor.Format.TAG_BOOTCLASSPATH;
/** Tag replaced with ProcessExecutors.getRepositoryPath () */
public static final String TAG_REPOSITORY = ProcessExecutor.Format.TAG_REPOSITORY;
/** Tag replaced with ProcessExecutors.getLibraryPath () */
public static final String TAG_LIBRARY = ProcessExecutor.Format.TAG_LIBRARY;
/** Tag replaced with arguments of the program */
public static final String TAG_FILES = "files"; // NOI18N
/** Tag replaced with install directory of JDK */
public static final String TAG_JAVAHOME = ProcessExecutor.Format.TAG_JAVAHOME;
/** Tag replaced with separator between filename components */
public static final String TAG_SEPARATOR = ProcessExecutor.Format.TAG_SEPARATOR;
/** Tag replaced with separator between path components */
public static final String TAG_PATHSEPARATOR = ProcessExecutor.Format.TAG_PATHSEPARATOR;
static final long serialVersionUID =-8630048144603405233L;
/** All values for the paths takes from NbClassPath.createXXX methods.
*
* @param files files to compile
*/
public Format (String[] files) {
this (
files,
NbClassPath.createClassPath (),
NbClassPath.createBootClassPath (),
NbClassPath.createRepositoryPath (FileSystemCapability.COMPILE),
NbClassPath.createLibraryPath ()
);
}
/** @param files files to compile
* @param classPath to substitute instead of CLASSPATH
* @param bootClassPath boot class path
* @param repository repository path
* @param library library path
*/
public Format (
String[] files,
NbClassPath classPath,
NbClassPath bootClassPath,
NbClassPath repository,
NbClassPath library
) {
super (createMap7 ());
java.util.Map map = getMap ();
map.put (TAG_CLASSPATH, classPath.getClassPath ());
map.put (TAG_BOOTCLASSPATH, bootClassPath.getClassPath ());
map.put (TAG_REPOSITORY, repository.getClassPath ());
map.put (TAG_LIBRARY, library.getClassPath ());
map.put (TAG_FILES, asParameterString (files));
map.put (TAG_JAVAHOME, System.getProperty ("java.home"));
map.put (TAG_SEPARATOR, File.separator);
map.put (TAG_PATHSEPARATOR, File.pathSeparator);
}
/** Helper method to allows conversion of list of files to compile to
* one string that can be passed as parameter to external process.
* On non Windows machines the method simply concatenates the strings
* into one. On Windows, if the file count it greater then ten, it
* creates temporary file, writes the strings into it and returns
* "@filename" witch is accepted by common programmers instead of the
* list of files.
*
* @param files array of files to compile
* @return the string representing the files to compile or null if it
* cannot be created (like the temporary file cannot be created)
*/
public static String asParameterString (String[] files) {
if (files.length > 10 && Utilities.isWindows()) {
File f = constructFile(files);
if (f == null) return null;
return "@" + f; // NOI18N
} else {
return constructString(files);
}
}
/** Creates default size hash map.
*/
private static java.util.HashMap createMap7 () {
return new java.util.HashMap (7);
}
/** prefix for a tmp file */
private static final String PREFIX = "compilerparams"; // NOI18N
/** suffix for a tmp file */
private static final String SUFFIX = "pms"; // NOI18N
/** @return File containing all files to compile. */
private static File constructFile(String[] files) {
try {
File f = File.createTempFile(PREFIX, SUFFIX);
f.deleteOnExit();
PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(f)));
Iterator iter = Arrays.asList (files).iterator();
while (iter.hasNext()) {
pw.println((String) iter.next());
}
pw.close();
return f;
} catch (IOException e) {
return null;
}
}
/** @return StringBuffer containing all files to compile. */
private static String constructString(String[] files) {
StringBuffer sb = new StringBuffer ();
String add = ""; // NOI18N
for (int i = 0; i < files.length; i++) {
sb.append (add);
if (files[i].indexOf(' ') >= 0) {
sb.append("\""); // NOI18N
sb.append(files[i]);
sb.append("\""); // NOI18N
} else {
sb.append (files[i]);
}
add = " "; // NOI18N
}
return sb.toString ();
}
}
}
/*
* Log
* 25 src-jtulach1.24 2/4/00 Ales Novak #5556
* 24 src-jtulach1.23 2/4/00 Jesse Glick Fix to permit
* ExternalCompiler's with new-style constructors (taking resource paths
* or file names) to work in an ExternalCompilerGroup.
* 23 src-jtulach1.22 1/24/00 Ales Novak #5180
* 22 src-jtulach1.21 1/12/00 Ian Formanek NOI18N
* 21 src-jtulach1.20 1/10/00 Ales Novak stopCompile action
* 20 src-jtulach1.19 1/10/00 Ales Novak #5180
* 19 src-jtulach1.18 1/8/00 Petr Jiricka Fixed
* NullPointerException (thrown when no compilers have been added to the
* group)
* 18 src-jtulach1.17 11/10/99 Ales Novak last change is rolled
* back
* 17 src-jtulach1.16 11/9/99 Ales Novak @ parameter using moved
* into java DO
* 16 src-jtulach1.15 11/8/99 Ales Novak better notification of
* exceptions
* 15 src-jtulach1.14 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 14 src-jtulach1.13 10/1/99 Jesse Glick ExternalCompilerGroup
* has process format consistent with ProcessExecutor.
* 13 src-jtulach1.12 9/29/99 Ales Novak isUpToDate check moved
* into CompilationEngine
* 12 src-jtulach1.11 9/10/99 Jesse Glick Small API change:
* ExternalCompiler.compilerType -> Compiler.compilerGroupKey.
* 11 src-jtulach1.10 9/10/99 Jaroslav Tulach compiles with jikes.
* 10 src-jtulach1.9 8/18/99 Ian Formanek Generated serial version
* UID
* 9 src-jtulach1.8 8/11/99 Ales Novak file names containing
* space bug
* 8 src-jtulach1.7 6/22/99 Jesse Glick Made start() nonfinal.
* 7 src-jtulach1.6 6/8/99 Ian Formanek ---- Package Change To
* org.openide ----
* 6 src-jtulach1.5 6/2/99 Jaroslav Tulach createProcess receives
* also compiler type.
* 5 src-jtulach1.4 6/2/99 Jaroslav Tulach ExternalCompiler has
* method for specifying its type.
* 4 src-jtulach1.3 5/31/99 Jaroslav Tulach External Execution &
* Compilation
* 3 src-jtulach1.2 4/1/99 Ales Novak
* 2 src-jtulach1.1 3/29/99 Jesse Glick [JavaDoc]
* 1 src-jtulach1.0 3/28/99 Ales Novak
* $
*/